#include "stdafx.h"
#include "XMemMgr.h"

#include <map>
#include <set>
#include "XStrUtil.h"

static BOOL     g_bInXMemMgrLogic = FALSE;
static BOOL     g_bXMemMgrValid = TRUE;

//////////////////////////////////////////////////////////////////////////
class XMemMgr
{
    XMemMgr(const XMemMgr&);
    XMemMgr& operator =(const XMemMgr&);
public:
    XMemMgr();
    ~XMemMgr();

    static XMemMgr& instance();

    void AddMemLog(void* ptr, LPCSTR szFilePath, int nLine, LPCSTR szTypeName, int nSize, int nCount);
    void DelMemLog(void* ptr);

    void    CheckMemLeak(BOOL bForce);
    void    SetBreakOnSize(size_t nSize);
    void    SetBreakOnMemLeak(BOOL bEnable);
    void    EnableMemLeakDetect(BOOL bEnable);

protected:
    typedef struct
    {
        std::string strFileName;
        int         nLine;
        std::string strTypeName;
        int         nSize;
        int         nCount;
    } stAllocLog;
    typedef std::map<void*, stAllocLog>     AllocLogMap;
    AllocLogMap         m_AllocLogMap;

    BOOL                m_bEnableMemLeak;
    BOOL                m_bBreakOnMemLeak;

    typedef std::set<size_t>                BreakSizeSet;
    BreakSizeSet        m_BreakSizeSet;
};

//////////////////////////////////////////////////////////////////////////
XMemMgr::XMemMgr()
{
    m_bEnableMemLeak = TRUE;
    m_bBreakOnMemLeak = TRUE;
    g_bXMemMgrValid = TRUE;
}
XMemMgr::~XMemMgr()
{
    g_bXMemMgrValid = FALSE;
    CheckMemLeak(FALSE);
}

XMemMgr& XMemMgr::instance()
{
    static XMemMgr _instance;
    return _instance;
}

void XMemMgr::AddMemLog(void* ptr, LPCSTR szFilePath, int nLine, LPCSTR szTypeName, int nSize, int nCount)
{
    BreakSizeSet::iterator IteSize = m_BreakSizeSet.find(nSize);
    if(IteSize != m_BreakSizeSet.end())
    {
        XASSERT_LOW(FALSE, _T("Specified Alloc Size"));
    }

    stAllocLog log;
    log.strFileName = szFilePath;
    log.nLine = nLine;
    log.strTypeName = szTypeName;
    log.nSize = nSize;
    log.nCount = nCount;

    std::string::size_type pos = log.strFileName.rfind('\\');
    if(pos == std::string::npos)
        pos = log.strFileName.rfind('/');
    if(pos != std::string::npos)
        log.strFileName = log.strFileName.substr(pos + 1);

    m_AllocLogMap.insert(AllocLogMap::value_type(ptr, log));
}

void XMemMgr::DelMemLog(void* ptr)
{
    AllocLogMap::iterator IteLog = m_AllocLogMap.find(ptr);
    if(IteLog != m_AllocLogMap.end())
    {
        m_AllocLogMap.erase(IteLog);
    }
    else
    {
        size_t nSize = 0;
        IteLog = m_AllocLogMap.begin();
        for(; IteLog != m_AllocLogMap.end(); ++ IteLog)
        {
            BYTE * const p = (BYTE * const)IteLog->first;
            nSize = IteLog->second.nCount * IteLog->second.nSize;
            if(ptr >= p && ptr < p + nSize)
            {
                break;
            }
        }
        if(IteLog != m_AllocLogMap.end())
        {
            m_AllocLogMap.erase(IteLog);
        }
        else
        {
            XASSERT_ERROR(FALSE, _T("Memory Log Not Found!"));
        }
    }
}

void XMemMgr::CheckMemLeak(BOOL bForce)
{
    if(!m_bEnableMemLeak && !bForce)
    {
        // printf("\r\n\r\nMemory Leak Detection Disabled!\r\n\r\n");
        // XOutputLog4(_T("MemLeak"), _T("\r\n\r\nMemory Leak Detection Disabled!\r\n\r\n"));
        return;
    }

    size_t nCount = m_AllocLogMap.size();
    if(nCount <= 0)
        return;

    if(m_bBreakOnMemLeak)
    {
        XASSERT_ERROR(FALSE, _T("Memory Leak Detected!"));
    }

    printf("\r\n\r\n//////////////////////////////////////////////////////////////////////////\r\n\r\n");
    XOutputLog4(_T("MemLeak"), _T("\r\n\r\n//////////////////////////////////////////////////////////////////////////\r\n\r\n"));

    printf("Skin Memory Leak Detected: \r\n");
    XOutputLog4(_T("MemLeak"), _T("Skin Memory Leak Detected: \r\n"));

//     std::string strFileName;
//     int         nLine;
//     std::string strTypeName;
//     int         nSize;
//     int         nCount;

    size_t nIndex = 0;
    AllocLogMap::iterator IteLog = m_AllocLogMap.begin();
    for(; IteLog != m_AllocLogMap.end(); ++ IteLog)
    {
        const void* const & pData = IteLog->first;
        const stAllocLog& Log = IteLog->second;
        printf("  %4d  0x%p\r\n"
                "        TypeName: %s, Size: %d, Count: %d\r\n"
                "        FileName: %s, Line: %d\r\n",
            nIndex,
            pData,
            Log.strTypeName.c_str(),
            Log.nSize,
            Log.nCount,
            Log.strFileName.c_str(),
            Log.nLine);
        XOutputLog4(_T("MemLeak"),
            _T("  %4d  0x%p, TypeName: %s, Size: %d, Count: %d, FileName: %s, Line: %d\r\n"),
            nIndex,
            pData,
            XStrUtil::AnsiToStr(Log.strTypeName.c_str()).GetData(),
            Log.nSize,
            Log.nCount,
            XStrUtil::AnsiToStr(Log.strFileName.c_str()).GetData(),
            Log.nLine);
        ++ nIndex;
    }

    printf("\r\n\r\n//////////////////////////////////////////////////////////////////////////\r\n\r\n");
    XOutputLog4(_T("MemLeak"), _T("\r\n\r\n//////////////////////////////////////////////////////////////////////////\r\n\r\n"));
}

void XMemMgr::SetBreakOnSize(size_t nSize)
{
    m_BreakSizeSet.insert(nSize);
}

void XMemMgr::SetBreakOnMemLeak(BOOL bEnable)
{
    m_bBreakOnMemLeak = bEnable;
}

void XMemMgr::EnableMemLeakDetect(BOOL bEnable)
{
    m_bEnableMemLeak = bEnable;
}

//////////////////////////////////////////////////////////////////////////

void AddMemLog(void* ptr, LPCSTR szFilePath, int nLine, LPCSTR szTypeName, int nSize, int nCount)
{
    XMemMgr::instance().AddMemLog(ptr, szFilePath, nLine, szTypeName, nSize, nCount);
}
void DelMemLog(void* ptr)
{
    XMemMgr::instance().DelMemLog(ptr);
}

// Memory
namespace Util
{
    ExportXSkinLib void    EnableMemLeakDetect(BOOL bEnable)
    {
        XMemMgr::instance().EnableMemLeakDetect(bEnable);
    }

    ExportXSkinLib void    CheckMemLeak()
    {
        XMemMgr::instance().CheckMemLeak(TRUE);
    }

    ExportXSkinLib void    SetBreakOnSize(size_t nSize)
    {
        XMemMgr::instance().SetBreakOnSize(nSize);
    }

    ExportXSkinLib void    SetBreakOnMemLeak(BOOL bEnable)
    {
        XMemMgr::instance().SetBreakOnMemLeak(bEnable);
    }
}
